package xyz.lwm.lazycat.beans;

import xyz.lwm.lazycat.utility.CollectionUtil;
import xyz.lwm.lazycat.utility.It;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Optional;

/**
 * bean design util
 *
 * @author lwm
 */
public abstract class BeanDesignUtil {

    /**
     * build design, 类可以是 default class 或者 public class. <br>
     * 构造器可以是 default 或 public, 优先选 public 构造器, 或者在其他构造器中选一个
     *
     * @param clz class
     * @return bean design
     */
    public static <T> It<BeanDesign<T>> buildDesign(Class<T> clz) {
        int mod = clz.getModifiers();
        if (!Modifier.isInterface(mod) && !Modifier.isAbstract(mod) &&
                !Modifier.isPrivate(mod) && !clz.isEnum() && !clz.isAnonymousClass()) {
            It<? extends Constructor<?>> it = It.of(clz.getConstructors())
                    .filter(CollectionUtil::isNotEmpty)
                    .let(c -> c[0]);
            if (!it.isPresent()) {
                Optional<Constructor<?>> first = Arrays.stream(clz.getDeclaredConstructors()).filter(c -> {
                    int modifiers = c.getModifiers();
                    return !Modifier.isPrivate(modifiers) && !Modifier.isAbstract(modifiers);
                }).findFirst();
                if (first.isPresent()) {
                    it = It.of(first.get());
                }
            }
            // 构造器可能是 public 或 default, 但不会使用 private 构造器
            Constructor<?> constructor = it.getOrThrow(() ->
                    new IllegalArgumentException("class " + clz.getName() + " has no valid constructor"));
            // register design
            BeanDesign<T> design = new BeanDesignBuilder<T>()
                    .instantiable(constructor)
                    .type(clz).build();
            return It.of(design);
        }
        return It.none();
    }

    /**
     * build design, 方法必须是 public 且不是 abstract
     *
     * @param method 创建bean对象的方法
     * @param targetDependency 方法所在类的信息
     * @return bean design
     */
    @SuppressWarnings("unchecked")
    public static <T> It<BeanDesign<T>> buildDesign(Method method, Dependency targetDependency) {
        int mod = method.getModifiers();
        if (Modifier.isPublic(mod) && !Modifier.isAbstract(mod)) {
            // register design
            BeanDesign<T> design = new BeanDesignBuilder<T>()
                    .type((Class<T>) method.getReturnType())
                    .instantiable(method, targetDependency).build();
            return It.of(design);
        }
        return It.none();
    }

}
